#include"list.h"

//函数实现


//游戏逻辑功能函数

//初始化蛇2个节点，蛇头随机出现在边框以内三格区域内,蛇尾在蛇头下方,参数head为蛇信息
void cre_snake(S **head)
{
    *head = malloc(sizeof(S));
    S *end = malloc(sizeof(S));
    srand(time(0));
    (*head)->x = rand() % 15 + 3;
    (*head)->y = rand() % 15 + 3;
    (*head)->next = end;
    end->x = (*head)->x;
    end->y = ((*head)->y) + 1;
    end->next = NULL;
}

//改进后的创建食物函数，在蛇位置以外区域随机生成一个新的食物，参数f为食物信息，head为蛇信息
void cre_food(F *f, S *head)
{
    int abl_use[20][20];
    S *find = head;
    int len = 1;
    int n;
    int i, j, key = 0;
    for (i = 0; i<20; i++)
        for (j = 0; j<20; j++)
            abl_use[i][j] = 0;
    while (find != NULL)
    {
        abl_use[find->y][find->x] = 1;
        find = find->next;
        len++;
    }

    srand(time(0));
    n = rand() % (399 - len);
    for (i = 0; (i<20) && (key<n); i++)
        for (j = 0; (j<20) && (key<n); j++)
        {
            if (abl_use[i][j] == 0)
                key++;
        }
    f->x = j - 1;
    f->y = i - 1;
}

//蛇向指定方向走一步，参数toward为指定方向，head为蛇信息
void move(char toward, S *head)
{
    S *find = head->next;
    int sx, sy, tx, ty;
    sx = find->x;
    sy = find->y;
    find->x = head->x;
    find->y = head->y;
    switch (toward)
    {
    case 'w':
        head->y -= 1;
        break;
    case 's':
        head->y += 1;
        break;
    case 'a':
        head->x -= 1;
        break;
    case 'd':
        head->x += 1;
        break;
    }
    while (find->next != NULL)
    {
        tx = find->next->x;
        ty = find->next->y;
        find->next->x = sx;
        find->next->y = sy;
        sx = tx;
        sy = ty;
        find = find->next;
    }
}

//避免移动中出现直接掉头的情况,参数now为新指定方向，fro当前方向
int if_move(char now,char fro)
{
    if(now=='w'&&fro=='s'||now=='s'&&fro=='w'||now=='a'&&fro=='d'||now=='d'&&fro=='a')
        return 0;
    else
        return 1;
}

//吃到食物时用来加长蛇的函数,在蛇尾方向上新增加一个节点，参数head为蛇信息
void l_snake(S *head)
{
    S *find = head;
    S *tmp = NULL;
    S *new = malloc(sizeof(S));
    new->next = NULL;
    while (find->next->next != NULL)
    {
        find = find->next;
    }
    tmp = find->next;
    tmp->next = new;
    new->x = tmp->x + tmp->x - find->x;
    new->y = tmp->y + tmp->y - find->y;
}

//判断蛇是否吃到食物，吃掉则调用l_snake加长蛇，并刷新食物位置，参数head为蛇信息，f为食物信息
void eat(S *head, F *f)
{
    if (head->x == f->x&&head->y == f->y)
    {
        l_snake(head);
        cre_food(f, head);
        //       while(if_foodinsnake(head,f))
        //           cre_food(f);
    }
}



//GAMEOVER部分

//判断蛇是否越界，参数head为蛇信息
int outside(S *head)
{
    if(head->x<0||head->y<0||head->x>19||head->y>19)
        return 1;
    else
        return 0;
}

//判断蛇是否碰到自己，参数head为蛇信息
int headisbody(S *head)
{
    S *find = head->next;
    while(find != NULL)
    {
        if(find->x==head->x&&find->y==head->y)
            return 1;
        find = find->next;
    }
    return 0;
}

//通过outside,headisbody判断是否gameover，并执行结束程序的相关处理
void gameover(S* head)
{
    if(headisbody(head)||outside(head))
    {
        system("clear");
        printf("Game Over!\n");
        recover_keyboard();
        exit(EXIT_SUCCESS);
    }
}

//改进之前用于判断新生成的食物是否在蛇身上
/*
int if_foodinsnake(S *head,F *f)
{
    S *find = head;
    while(find->next!=NULL)
    {
        if(find->x!=f->x||find->y!=f->y)
            find = find->next;
        else
            return 1;
    }
        return 0;
}*/

//画图相关函数

//以200 * 200像素位置为坐标原点, 屏幕正下方为y正半轴，屏幕正右方为x正半轴。
//根据参数中的xx, yy是有意中的坐标，画图时对应到像素的x, y上作为矩形左上角顶点，画一个10 * 20像素的矩形，参数r, g, b用于颜色设定。
//addr为操作帧缓冲的指针，之后再利用该函数来画蛇和食物。
void draw_point(unsigned long* addr,int xx,int yy,int r,int g,int b)
{
    int i,j,x,y;
    x = xx*10+200;
    y = yy*20+200;
        for(i=y; i<y+20; i++)
            for(j=x; j<x+10; j++)
                    *(addr + i*688 + j) = RGB888(r,g,b);
}

//根据蛇每个节点的坐标画蛇，蛇头为绿色，蛇身为红色
void draw_snake(unsigned long* addr,S *head)
{
    S *find = head;
    draw_point(addr,find->x,find->y,0,0xff,0);
    find = find->next;
    while(find!=NULL)
    {
        draw_point(addr,find->x,find->y,0xff,0,0);
        find = find->next;
    }
}

//根据食物坐标画一个绿色的食物
void draw_food(unsigned long* addr,F *f)
{
    draw_point(addr,f->x,f->y,0,0xff,0);
}

//游戏边框，屏幕坐上为坐标原点，向右为x正半轴，向下为y正半轴
//屏幕的像素范围为(0,0)到(688,，768)
//游戏边框颜色为蓝色，内侧为(200,200)到(600,400)矩形区域
void draw_side(unsigned long* addr)
{
        int i , j;
//边框的上下边
        for(i=180; i<200; i++)
            for(j=190; j<410; j++)
                    *(addr + i*688 + j) = RGB888(0,0,0xff);

        for(i=600; i<620; i++)
            for(j=190; j<410; j++)
                    *(addr + i*688 + j) = RGB888(0,0,0xff);

//边框的左右边
        for(i=200; i<600; i++)
            for(j=190; j<200; j++)
                    *(addr + i*688 + j) = RGB888(0,0,0xff);

        for(i=200; i<600; i++)
            for(j=400; j<410; j++)
                    *(addr + i*688 + j) = RGB888(0,0,0xff);

}

//清空边框内侧区域，全部刷黑
void myclear(unsigned long *addr)
{
    int i, j;
    for (i = 200; i<600; i++)
        for (j = 200; j<400; j++)
            *(addr + i * 688 + j) = RGB888(0, 0, 0);
}

//读键盘按键
int init_keyboard(void)
{
    int ret;
    struct termios tc;
    ret = tcgetattr(0, &tcsave);
    if(ret < 0)
        return -1;
    tc = tcsave;
    tc.c_lflag &= ~(ECHO|ICANON);
    ret = tcsetattr(0, TCSANOW, &tc);
    if(ret < 0)
        return -1;
    flsave = fcntl(0, F_GETFL);
    fcntl(0, F_SETFL, flsave|O_NONBLOCK);
    return 0;
}
void recover_keyboard(void)
{
    tcsetattr(0, TCSANOW, &tcsave);
    fcntl(0, F_SETFL, flsave);
}
int get_key(void)
{
    unsigned char buf[3];

    int ret = read(0, buf, sizeof(buf));
    if(ret < 0)
        return -1;

    int i = 0, key = 0;
    for(i=0; i<ret; i++){
        key += (buf[i]<<(i*8));
    }
    return key;
}
int is_up(int key)
{
    return key == UP;
}
int is_down(int key)
{
    return key == DOWN;
}
int is_left(int key)
{
    return key == LEFT;
}
int is_right(int key)
{
    return key == RIGHT;
}
int is_enter(int key)
{
    return key == ENTER;
}
int is_esc(int key)
{
    return key == ESC;
}
int is_space(int key)
{
    return key == SPACE;
}
